/**
 * Star.java
 *
 * Roslan's Remake of MOO in 3D using Ardor3D
 * (C) 2011 Roslan Amir. All rights reserved.
 */

package com.roslan.games.moo3d.data;

import com.ardor3d.math.MathUtils;

/**
 * This class implements the Star entity. It contains all the attributes for an uncolonized star.
 * 
 * @author Roslan Amir
 * @version 1.0 - Jan 4, 2011
 */
public class Star {

	/**
	 * Constants
	 */
	public static final String[] STAR_NAMES = {
		"Orion",	// Orion (0)
		"Sol",		// Human (1)
		"Fieras",	// Mrrshan (2)
		"Cryslon",	// Silicoid (3)
		"Sssla",	// Sakkra (4)
		"Mentar",	// Psilon (5)
		"Altair",	// Alkari (6)
		"Kholdan",	// Klackon (7)
		"Ursa",		// Bulrathi (8)
		"Meklon",	// Meklar (9)
		"Nazin",	// Darlok (10)
		"Ajax", "Anraq", "Antares", "Aquilae", "Argus", "Arietis", "Artemis", "Aurora", "Berel", "Beta Ceti", "Bootis", "Celtsi", "Centauri", "Collassa", "Crius", "Crypto", "Cygni",
		"Darrian", "Denubius", "Dolz", "Draconis", "Drakka", "Dunatis", "Endoria", "Escalon", "Esper", "Exis", "Firma", "Galos", "Gienah", "Gion", "Gorra", "Guradas", "Helos",
		"Herculis", "Hyades", "Hyboria", "Imra", "Incedius", "Iranha", "Jinga", "Kailis", "Kakata", "Keeta", "Klystron", "Kronos", "Kulthos", "Laan", "Lyae", "Maalor", "Maretta",
		"Misha", "Mobas", "Moro", "Morrig", "Mu Delphi", "Neptunus", "Nitzer", "Nordia", "Nyarl", "Obaca", "Omicron", "Paladia", "Paranar", "Phantos", "Phyco", "Pollus", "Primodius",
		"Proteus", "Proxima", "Quayal", "Rana", "Rayden", "Regulus", "Reticuli", "Rha", "Rhilus", "Rigel", "Romulas", "Rotan", "Ryoun", "Seidon", "Selia", "Simius", "Spica", "Stalaz",
		"Talas", "Tao", "Tau Cygni", "Tauri", "Thrax", "Toranor", "Trax", "Tyr", "Ukko", "Uxmai", "Vega", "Volantis", "Vox", "Vulcan", "Whynil", "Willow", "Xendalla", "Xengara",
		"Xudax", "Yarrow", "Zhardan", "Zoctan" };

	/**
	 * Constants
	 */
	public static final String[] STAR_DESCRIPTION = {
		"Yellow stars offer the best chance of discovering\nterran and sub-terran planets",
		"Green stars are moderately bright and\nhave a wide range of planetary types",
        "Red stars are old, dull stars that\ncommonly have poor planets",
        "White stars burn incredibly hot and\ngenerally have hostile planets",
        "Blue stars are relatively young stars\nwith mineral rich lifeless planets",
        "Neutron stars are rare and offer the\ngreatest chance of finding rich planets"
    };

	/**
	 * The default planet size for Orion.
	 */
	protected static final int ORION_PLANET_SIZE = 120;

	/**
	 * The default planet size for each homeworld.
	 */
	protected static final int HOMEWORLD_PLANET_SIZE = 100;

	/**
	 * The number of BC produced per production point by Ultra Rich planets.
	 */
	protected static final double BC_PER_PRODUCTION_ULTRA_RICH = 3.0f; // thrice

	/**
	 * The number of BC produced per production point by Rich planets.
	 */
	protected static final double BC_PER_PRODUCTION_RICH = 2.0f; // two

	/**
	 * The number of BC produced per production point by Normal planets.
	 */
	protected static final double BC_PER_PRODUCTION_NORMAL = 1.0f; // one

	/**
	 * The number of BC produced per production point by Poor planets.
	 */
	protected static final double BC_PER_PRODUCTION_POOR = 0.5f; // one half

	/**
	 * The number of BC produced per production point by Ultra Poor planets.
	 */
	protected static final double BC_PER_PRODUCTION_ULTRA_POOR = 1.0f/3.0f; // one third

	/**
	 * The growth rate for hostile planets.
	 */
	protected static final double GROWTH_RATE_HOSTILE = 0.5f;

	/**
	 * The growth rate for normal planets.
	 */
	protected static final double GROWTH_RATE_NORMAL = 1.0f;

	/**
	 * The growth rate for fertile planets.
	 */
	protected static final double GROWTH_RATE_FERTILE = 1.5f;

	/**
	 * The growth rate for gaia planets.
	 */
	protected static final double GROWTH_RATE_GAIA = 2.0f;

	/**
	 * The number of RP produced per production point by Orion.
	 */
	protected static final int RP_PER_PRODUCTION_ORION = 4;

	/**
	 * The number of RP produced per production point by Artifacts planets.
	 */
	protected static final int RP_PER_PRODUCTION_ARTIFACTS = 2;

	/**
	 * The number of RP produced per production point by Normal planets.
	 */
	protected static final int RP_PER_PRODUCTION_NORMAL = 1;

	/**
	 * The star index into the array of star names
	 */
	public int starIndex;

	/**
	 * The index into the array of colors for the stars (not the same as the race's flag color)
	 */
	public int colorIndex;

	/**
	 * The name of this star or colony (may be different than the original star name).
	 */
	public String name;

	/**
	 * The size of the single habitable planet in this star system (equivalent to the inital max population on this planet)
	 */
	public int planetSize;

	/**
	 * The starting (and current) planetary environment
	 */
	public Environment environment;

	/**
	 * Fertility: Hostile, Normal, Fertile, Gaia
	 */
	public Fertility fertility;

	/**
	 * Star's mineral contents: Ultra Poor, Poor, Normal, Rich, Ultra Rich
	 */
	public Minerals minerals;

	/**
	 * Research potential: Normal, Artifacts, Orion
	 */
	public Research research;

	/**
	 * String listing of the specials (fertility, minerals, research)
	 */
	public String specials;

	/**
	 * How many BC per production for this planet.
	 */
	protected double bcPerProduction;

	/**
	 * The growth rate of this planet based on its Fertility.
	 */
	protected double growthRate;

	/**
	 * How much research points per production.
	 */
	protected int rpPerProduction;

	/**
	 * Whether the planet has been explored by the player
	 */
	public boolean explored;

	/**
	 * The race who owns this star.
	 */
	public Race owner;

	/**
	 * Boolean array to indicate which star names have been taken.
	 */
	private static final boolean[] assigned = new boolean[STAR_NAMES.length - 11];  // minus Orion and the 10 race home stars

	/**
	 * Constructor.
	 * 
	 * @param starIndex - the index into the <code>STAR_NAMES</code> array for this star.
	 * @param colorIndex - the index into the <code>STAR_DESCRIPTION</code> array for this star.
	 */
	protected Star(int starIndex, int colorIndex) {
		this.starIndex = starIndex;
		this.colorIndex = colorIndex;
		this.name = STAR_NAMES[starIndex];
	}

	/**
	 * Create the <code>Star</code> instance represeting the ancient star Orion.
	 *
	 * @return the <code>Star</code> object representing the Orion star system
	 */
	public static Star createOrion() {
		/*
		 * Randomly determine the star color index, hence its color and description.
		 */
		int colorIndex = MathUtils.nextRandomInt(0, 20) / 4; // 0-3=Yellow,4-7=Green,8-11=Red,12-15=White,16-19=Blue,20=Purple

		Star orion = new Star(0, colorIndex);				// Orion is the first star listed
		orion.planetSize = ORION_PLANET_SIZE;
		orion.environment = Environment.TERRAN;
		orion.fertility = Fertility.NORMAL;
		orion.minerals = Minerals.NORMAL;
		orion.research = Research.ORION;
		orion.bcPerProduction = BC_PER_PRODUCTION_NORMAL;
		orion.growthRate = GROWTH_RATE_NORMAL;
		orion.rpPerProduction = RP_PER_PRODUCTION_ORION;
		orion.specials = orion.makeSpecials();
		orion.explored = true; // temporary for testing
		return orion;
	}

	/**
	 * Create the <code>StarNode</code> object representing a random star system.
	 * 
	 * @return the <code>Star</code> object representing the random star system
	 */
	public static Star createRandomStar() {
		/*
		 * Randomly determine the star index, hence its name.
		 */
		int index = MathUtils.nextRandomInt(0, assigned.length - 1); // random(min,max) is inclusive
		while (assigned[index]) {
			if (++index == assigned.length)
				index = 0;
		}
		assigned[index] = true;
		int starIndex = index + 11; // offset to cater for Orion and 10 home stars

		/*
		 * Randomly determine the star color index, hence its color and description.
		 */
		int colorIndex = MathUtils.nextRandomInt(0, 20) / 4; // 0-3=Yellow,4-7=Green,8-11=Red,12-15=White,16-19=Blue,20=Purple

		/*
		 * Create the star.
		 */
		Star star = new Star(starIndex, colorIndex);

		/*
		 * Randomly determine the planet type based on the star color.
		 */
		Environment[] envValues = Environment.values();
		int roll = 0;
		switch (star.colorIndex) {
		/*
		 * Highest chances of finding terran/subterran planets
		 */
		case 0: // yellow
			roll = MathUtils.nextRandomInt(0, 36);
			if (roll < 12) {											// 0-11 = hostile
				star.environment = envValues[roll / 2];					// 0-5 = radiated-barren
			} else if (roll < 20) {										// 12-20 = minimal,desert,steppe
				star.environment = envValues[((roll - 12) / 3) + 6];	// 6-8 = minimal,desert,steppe
			} else if (roll < 35) {										// 20-35 = arid,ocean,jungle,terran
				star.environment = envValues[((roll - 20) / 4) + 9];	// 9-12 = arid,ocean,jungle,terran
			} else {													// 36
				star.environment = Environment.NONE;					// 13 = none
			}
			break;
		/*
		 * Second highest chances of finding terran/subterran planets
		 */
		case 1: // green
			roll = MathUtils.nextRandomInt(0, 36);
			if (roll < 12) {											// 0-11 = hostile
				star.environment = envValues[roll / 2];					// 0-5 = radiated-barren
			} else if (roll < 24) {										// 12-23 = minimal,desert,steppe
				star.environment = envValues[((roll - 12) / 4) + 6];	// 6-8 = minimal,desert,steppe
			} else if (roll < 35) {										// 24-35 = arid,ocean,jungle,terran
				star.environment = envValues[((roll - 24) / 3) + 9];	// 9-12 = arid,ocean,jungle,terran
			} else {													// 36
				star.environment = Environment.NONE;					// 13 = none
			}
			break;
		/*
		 * Equal chances of finding all planet types
		 */
		case 2: // red
			roll = MathUtils.nextRandomInt(0, 26);
			if (roll < 26) {											// 0-25 = all
				star.environment = envValues[roll / 2];					// 0-12 = all
			} else {													// 26
				star.environment = Environment.NONE;					// 13 = none
			}
			break;
		/*
		 * Higher chances of finding hostile (lifeless) planets, esp inferno, toxic and radiated
		 */
		case 3: // white
			roll = MathUtils.nextRandomInt(0, 32);
			if (roll < 12) {											// 0-11 = radiated,toxic,inferno
				star.environment = envValues[roll / 4];					// 0-2 = radiated,toxic,inferno
			} else if (roll < 32) {										// 12-31 = rest
				star.environment = envValues[((roll - 12) / 2) + 3];	// 3-12 = rest
			} else {													// 32
				star.environment = Environment.NONE;					// 13 = none
			}
			break;
		/*
		 * Higher chances of finding hostile (lifeless) planets
		 */
		case 4: // blue
		case 5: // purple/neutron
			roll = MathUtils.nextRandomInt(0, 38);
			if (roll < 24) {											// 0-23 = hostile
				star.environment = envValues[roll / 4];					// 0-5 = radiated-barren
			} else if (roll < 38) {										// 24-37 = terran/subterran
				star.environment = envValues[((roll - 24) / 2) + 6];	// 6-12 = minimal-terran
			} else {													// 38
				star.environment = Environment.NONE;					// 13 = none
			}
			break;
		default:
			break;
		}

		/*
		 * Randomly assign the planet size based on the planet environment.
		 */
		roll = MathUtils.nextRandomInt(0, 11); // 0-11
		switch (star.environment) {
		case RADIATED:
			star.planetSize = roll * 5 + 10;
			break;
		case TOXIC:
			star.planetSize = roll * 5 + 15;
			break;
		case INFERNO:
			star.planetSize = roll * 5 + 20;
			break;
		case DEAD:
			star.planetSize = roll * 5 + 25;
			break;
		case TUNDRA:
			star.planetSize = roll * 5 + 30;
			break;
		case BARREN:
			star.planetSize = roll * 5 + 35;
			break;
		case MINIMAL:
			star.planetSize = roll * 5 + 40;
			break;
		case DESERT:
			star.planetSize = roll * 5 + 45;
			break;
		case STEPPE:
			star.planetSize = roll * 5 + 50;
			break;
		case ARID:
			star.planetSize = roll * 5 + 55;
			break;
		case OCEAN:
			star.planetSize = roll * 5 + 60;
			break;
		case JUNGLE:
			star.planetSize = roll * 5 + 65;
			break;
		case TERRAN:
			star.planetSize = roll * 5 + 70;
			break;
		default: // NONE
			star.planetSize = 0;
			break;
		}

		/*
		 * Randomly assign the fertility and research potential based on the planet environment.
		 */
		switch (star.environment) {
		case MINIMAL:
		case DESERT:
		case STEPPE:
		case ARID:
		case OCEAN:
		case JUNGLE:
		case TERRAN:
			star.fertility = (MathUtils.nextRandomInt(0, 4) == 0) ? Fertility.FERTILE : Fertility.NORMAL; // 0=Fertile, 1-4=Normal
			star.research = (MathUtils.nextRandomInt(0, 9) == 0) ? Research.ARTIFACTS : Research.NORMAL; // 0=Artifacts, 1-9=Normal
			break;
		default:
			star.fertility = Fertility.HOSTILE;
			star.research = Research.NORMAL;
			break;
		}

		/*
		 * Assign the population growth rate based on the fertility. No naturally occurring GAIA planets.
		 */
		switch (star.fertility) {
		case FERTILE:
			star.growthRate = GROWTH_RATE_FERTILE;
			break;
		case NORMAL:
			star.growthRate = GROWTH_RATE_NORMAL;
			break;
		default:
			star.growthRate = GROWTH_RATE_HOSTILE;
			break;
		}

		/*
		 * Assign the research rate based on the research capability.
		 */
		star.rpPerProduction = (star.research == Research.ARTIFACTS) ? RP_PER_PRODUCTION_ARTIFACTS : RP_PER_PRODUCTION_NORMAL;

		/*
		 * Randomly assign the mineral contents based on the star color.
		 */
		switch (star.colorIndex) {
		case 2: // Red stars are old, dull stars that commonly have poor planets.
			roll = MathUtils.nextRandomInt(0, 6);				// 0-6
			if (roll < 3) {
				star.minerals = Minerals.ULTRA_POOR;			// 0-2 = ultra poor
			} else if (roll < 6) {
				star.minerals = Minerals.POOR;					// 3-5 = poor
			} else {
				star.minerals = Minerals.NORMAL;				// 6 = normal
			}
			break;
		case 4: // Blue stars are relatively young stars with mineral rich lifeless planets.
			if (star.fertility == Fertility.HOSTILE) {
				roll = MathUtils.nextRandomInt(0, 12);			// 0-12
				if (roll < 3) {
					star.minerals = Minerals.ULTRA_RICH;		// 0-2 = ultra rich
				} else if (roll < 12) {
					star.minerals = Minerals.RICH;				// 3-11 = rich
				} else {
					star.minerals = Minerals.NORMAL;			// 12 = normal
				}
			} else {
				star.minerals = Minerals.NORMAL;
			}
			break;
		case 5: // Neutron stars are rare and offer the greatest chance of finding ultra rich planets.
			if (star.fertility == Fertility.HOSTILE) {
				roll = MathUtils.nextRandomInt(0, 12);			// 0-12
				if (roll < 9) {
					star.minerals = Minerals.ULTRA_RICH;		// 0-8 = ultra rich
				} else if (roll < 12) {
					star.minerals = Minerals.RICH;				// 9-11 = rich
				} else {
					star.minerals = Minerals.NORMAL;			// 12 = normal
				}
			} else {
				star.minerals = Minerals.NORMAL;
			}
			break;
		default: // Others
			roll = MathUtils.nextRandomInt(0, 19);				// 0-19
			if (roll < 1) {
				star.minerals = Minerals.ULTRA_RICH;			// 0 = ultra rich
			} else if (roll < 3) {
				star.minerals = Minerals.RICH;					// 1-2 = rich
			} else if (roll < 17) {
				star.minerals = Minerals.NORMAL;				// 3-16 = normal
			} else if (roll < 19) {
				star.minerals = Minerals.POOR;					// 17-18 = poor
			} else {
				star.minerals = Minerals.ULTRA_POOR;			// 19 = ultra poor
			}
			break;
		}

		/*
		 * Assign the rate of production based on the planet's minerals.
		 */
		switch (star.minerals) {
		case ULTRA_POOR:
			star.bcPerProduction = BC_PER_PRODUCTION_ULTRA_POOR; // one third
			break;
		case POOR:
			star.bcPerProduction = BC_PER_PRODUCTION_POOR; // one half
			break;
		case NORMAL:
			star.bcPerProduction = BC_PER_PRODUCTION_NORMAL; // one
			break;
		case RICH:
			star.bcPerProduction = BC_PER_PRODUCTION_RICH; // twice
			break;
		default:
			star.bcPerProduction = BC_PER_PRODUCTION_ULTRA_RICH; // thrice
		}

		/*
		 * Set the text strings for each attribute.
		 */
//		star.environmentText = star.environment.toString();
//		star.planetSizeText = String.valueOf(star.planetSize);
		star.specials = star.makeSpecials();

		return star;
	}

	/**
	 * Create a string representing the specialsLabel for this planet/star.
	 *
	 * @return
	 */
	protected String makeSpecials() {
		boolean first = true;
		StringBuilder sb = new StringBuilder();
		if (fertility != Fertility.NORMAL) {
			first = false;
			sb.append(fertility);
		}
		if (minerals != Minerals.NORMAL) {
			if (first)
				first = false;
			else
				sb.append(", ");
			sb.append(minerals);
		}
		if (research != Research.NORMAL) {
			if (first)
				first = false;
			else
				sb.append(", ");
			sb.append(research);
		}
		sb.append(" "); // apend a space character at the end
		return sb.toString();
	}

	/**
	 * Star environments.
	 */
	public static enum Environment {
		RADIATED { // 0
			public String toString() {
				return "Radiated";
			}
		},
		TOXIC { // 1
			public String toString() {
				return "Toxic";
			}
		},
		INFERNO { // 2
			public String toString() {
				return "Inferno";
			}
		},
		DEAD { // 3
			public String toString() {
				return "Dead";
			}
		},
		TUNDRA { // 4
			public String toString() {
				return "Tundra";
			}
		},
		BARREN { // 5
			public String toString() {
				return "Barren";
			}
		},
		MINIMAL { // 6
			public String toString() {
				return "Minimal";
			}
		},
		DESERT { // 7
			public String toString() {
				return "Desert";
			}
		},
		STEPPE { // 8
			public String toString() {
				return "Steppe";
			}
		},
		ARID { // 9
			public String toString() {
				return "Arid";
			}
		},
		OCEAN { // 10
			public String toString() {
				return "Ocean";
			}
		},
		JUNGLE { // 11
			public String toString() {
				return "Jungle";
			}
		},
		TERRAN { // 12
			public String toString() {
				return "Terran";
			}
		},
		NONE { // 13
			public String toString() {
				return "None";
			}
		},
	}

	/**
	 * Star fertility values.
	 */
	public static enum Fertility {
		/*
		 * Hostile planets have 1/2 the normal population growth rate.
		 */
		HOSTILE {
			public String toString() {
				return "Hostile";
			}
		},
		/*
		 * Normal planets have the normal population growth rate.
		 */
		NORMAL {
			public String toString() {
				return "Normal";
			}
		},
		/*
		 * Fertile planets have twice the normal population growth rate.
		 */
		FERTILE {
			public String toString() {
				return "Fertile";
			}
		},
		/*
		 * Gaia planets have twice the normal population growth rate.
		 */
		GAIA {
			public String toString() {
				return "Gaia";
			}
		}
	}

	/**
	 * Star mineral contents.
	 */
	public static enum Minerals {
		ULTRA_POOR {
			public String toString() {
				return "Ultra Poor";
			}
		},
		POOR {
			public String toString() {
				return "Poor";
			}
		},
		NORMAL {
			public String toString() {
				return "Normal";
			}
		},
		RICH {
			public String toString() {
				return "Rich";
			}
		},
		ULTRA_RICH {
			public String toString() {
				return "Ultra Rich";
			}
		}
	}

	/**
	 * Star research potential.
	 */
	public static enum Research {
		NORMAL {
			public String toString() {
				return "Normal";
			}
		},
		ARTIFACTS {
			public String toString() {
				return "Artifacts";
			}
		},
		ORION {
			public String toString() {
				return "Orion (4 x Tech)";
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "Star [name=" + this.name + 
				   ", colorIndex=" + this.colorIndex + 
				   ", planetSize=" + this.planetSize + 
				   ", environment=" + this.environment + 
				   ", fertility=" + this.fertility + 
				   ", minerals=" + this.minerals + 
				   ", research=" + this.research + 
				   ", specials=" + this.specials + 
				   ", bcPerProduction=" + this.bcPerProduction + 
				   ", growthRate=" + this.growthRate + 
				   ", rpPerProduction=" + this.rpPerProduction + 
				   ", explored=" + this.explored + 
				   ", owner=" + this.owner + "]";
	}

}
